package com.me.component.utils;

import ma.glasnost.orika.BoundMapperFacade;
import ma.glasnost.orika.MapperFactory;
import ma.glasnost.orika.impl.DefaultMapperFactory;
import ma.glasnost.orika.metadata.ClassMapBuilder;
import ma.glasnost.orika.metadata.MapperKey;
import ma.glasnost.orika.metadata.TypeFactory;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Objects;

/**
 * @author me
 * @DateTime 2021/11/18 16:38
 * @Description 类拷贝
 */
public final class Mapper {
    private Mapper() {
    }

    private static final MapperFactory MAPPER_FACTORY = new DefaultMapperFactory.Builder().build();

    private static final Object CLASS_MAP_REGISTER_LOCK = new Object();

    public static <S, T> List<T> map(Collection<S> source, Class<T> target) {
        Objects.requireNonNull(source, "source must be not null");
        List<T> list = new ArrayList<>(source.size());
        for (Object o : source) {
            T result = map(o, target);
            list.add(result);
        }

        return list;
    }

    public static <S, T> T map(S source, Class<T> target) {
        Objects.requireNonNull(source, "source must be not null");
        Class<S> sourceClass = (Class<S>) source.getClass();
        MapperKey mapperKey = getMapperKey(target, sourceClass);
        if (MAPPER_FACTORY.getClassMap(mapperKey) == null) {
            synchronized (CLASS_MAP_REGISTER_LOCK) {
                if (MAPPER_FACTORY.getClassMap(mapperKey) == null) {
                    registerClassMap(target, sourceClass);
                }
            }
        }
        BoundMapperFacade<S, T> mapperFacade = MAPPER_FACTORY.getMapperFacade(sourceClass, target);
        return mapperFacade.map(source);
    }

    private static <S, T> MapperKey getMapperKey(Class<T> tc, Class<S> sc) {
        return new MapperKey(TypeFactory.valueOf(sc), TypeFactory.valueOf(tc));
    }

    private static <S, T> void registerClassMap(Class<T> tc, Class<S> sc) {
        ClassMapBuilder<S, T> classMapBuilder = MAPPER_FACTORY.classMap(sc, tc);
        classMapBuilder.byDefault().register();
    }
}
